package br.com.JZSolution.domineduplex.controle;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import br.com.JZSolution.domineduplex.DomineDuplex;
import br.com.JZSolution.domineduplex.exception.CadastroException;
import br.com.JZSolution.domineduplex.utils.XmlUtils;
import br.com.JZSolution.domineduplexserver.modelo.Usuario;

import com.sun.org.apache.xpath.internal.XPathAPI;
  
public class ControladorDeUsuario {
	
	private static ControladorDeUsuario instance;
	
	/**
	 * Operações com o servidor
	 */
	public static final String CADASTRAR = "cadastrar";
	public static final String AUTENTICAR = "autenticar";
	public static final String SAIR = "sair";
	public static final String ADICIONAR_CONTATO = "adicionarContato";
	public static final String REMOVER_CONTATO = "removerContato";
	
	private List<Usuario> listaContatos;
	
	private ControladorDeUsuario(){
		instance = this;
	};
	public static ControladorDeUsuario getInstance(){
		if(instance == null) new ControladorDeUsuario();
		return instance;
	}
	
	public String cadastrarUsuario(Usuario usuario) throws CadastroException,MalformedURLException,IOException{
		
		System.out.println("CADASTRANDO USUARIO");
    	
		Map<String, String> mapa= usuario.propriedades();
		
		return (String)comunicarComServidor(CADASTRAR, mapa);
	
	}
	
	/**
	 * Adiciona um contato ao usuario autenticado.
	 * @param email do contato que se deseja adicionar.
	 */
	public void adicionarContato(String email) {
		
		Usuario usuarioAutenticado = DomineDuplex.getInstance().getUsuarioAutenticado();
		
		Map<String, String> mapa = new HashMap<String, String>();
		mapa.put("emailUsuario", usuarioAutenticado.getEmail());
		mapa.put("emailContato", email);
		
		String resposta = (String)comunicarComServidor(ADICIONAR_CONTATO, mapa);
		
		Document doc;
		try {
			doc = XmlUtils.StringToXml(resposta);
			Node temp =  XPathAPI.selectSingleNode(doc.getDocumentElement(), "contatos");
			if(temp != null){
				Node contatoNode = XPathAPI.selectSingleNode(temp, "contato");
				Usuario usrContato = usuarioNodeToObjeto(contatoNode);
				System.out.println("Usuario = "+usrContato.getEmail());
				DomineDuplex.getInstance().getMainframe().getPainelContatos().adicionarContato(usrContato);
			}else{
				System.out.println("Não foi salvo");
			}
			
		}catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("resp = "+resposta);
	}
	
	public void removerContato(Usuario usr) {
		
		Usuario usuarioAutenticado = DomineDuplex.getInstance().getUsuarioAutenticado();

		DomineDuplex.getInstance().getMainframe().getPainelContatos().removerContato(usr);
		
		Map<String, String> mapa = new HashMap<String, String>();
		mapa.put("emailUsuario", usuarioAutenticado.getEmail());
		mapa.put("emailContato", usr.getEmail());
		
		String resposta = (String)comunicarComServidor(REMOVER_CONTATO, mapa);
		
		System.out.println("resp = "+resposta);
	}
	
	
	/**
	 * Autentica o usuario colocado nas preferências, se este estiver preenchido
	 * retorna a mensagem de sucesso ou fracasso
	 */
	public String  autenticarUsuario() {
		ControladorDePreferencias cdp = ControladorDePreferencias.getInstance();		
		if(Boolean.parseBoolean(cdp.getPropriedade("salvarUsuarioSenha"))){ 
			String login = cdp.getPropriedade("login");
			String senha = cdp.getPropriedade("senha");
			return  autenticarUsuario(login,  senha);
		}else{
			return  "ok";
		}
	}
	
	/**
	 * Autentica com o usuario e senha, chamado pelo painelDeLogin nas preferências atualizar contatos.
	 * @param login
	 * @param senha
	 * retorna a mensagem de sucesso ou fracasso
	 */
	public String autenticarUsuario(String login, String senha) {
		
		if( login != null && !login.equals("") && login.contains("@") && login.contains(".")){
			if(!senha.equals("") && senha != null ){
				Usuario usr = new Usuario(login,senha);
				usr.setPorta(Integer.parseInt(ControladorDePreferencias.getInstance().getPropriedade(ControladorDePreferencias.PORTA)));
				
				String msg = "";
				String respostaXml = (String)comunicarComServidor(AUTENTICAR, usr.propriedades());
				limparContatos();
				if(respostaXml != null && respostaXml.trim().length() > 0){
					Document doc;
					try {
						doc = XmlUtils.StringToXml(respostaXml);
						Node erro = XPathAPI.selectSingleNode(doc.getDocumentElement(), "mensagem");
						if (erro == null || erro.getTextContent().trim().equals("") || erro.getTextContent().trim().equals("Autenticado com sucesso.") ){
							Node contatos = XPathAPI.selectSingleNode(doc.getDocumentElement(), "contatos");
							if(contatos != null && contatos.hasChildNodes()){
								carregarContatos(contatos);
								DomineDuplex.getInstance().setUsuarioAutenticado(usr);
								msg = "ok";
	
							}else{
								msg = "ok";//"Não há contatos há serem listados.";
							}
							DomineDuplex.getInstance().conectarDesconectar();
						}else{
							msg = erro.getTextContent();
						}
					} catch (ParserConfigurationException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					} catch (SAXException e) {
						e.printStackTrace();
					} catch (TransformerException e) {
						e.printStackTrace();
					}
					
					return msg;
				}else{
					return "Erro no servidor! Tente novamente!";
				}
			}else{
				return "Senha inválida!";
			}
		}else{
			return "Login inválido!";
		}	
		
	}
	
	private void limparContatos() {
		DomineDuplex.getInstance().getMainframe().getPainelContatos().limparListaContatos();
	}
	/**
	 * Carrega a lista de contatos com o Xml recebido
	 * @param xmlContatos
	 */
	private void carregarContatos(Node contatos) {
		
		listaContatos = new ArrayList<Usuario>();
		try {
			NodeList usuarioListNode = XPathAPI.selectNodeList(contatos, "contato");
			Node usuarioNode = null;
			Usuario usr = null;
			for (int i = 0; i < usuarioListNode.getLength(); i++){
				usuarioNode = usuarioListNode.item(i);
				usr = usuarioNodeToObjeto(usuarioNode);
				listaContatos.add(usr);
			}
			DomineDuplex.getInstance().getMainframe().getPainelContatos().atualizarContatos(listaContatos);
			
		}catch (TransformerException e) {
			e.printStackTrace();
		}
	}
	
	private Usuario usuarioNodeToObjeto(Node usuarioNode) throws TransformerException {
		Usuario usr = new Usuario();
		usr.setNome(XPathAPI.selectSingleNode(usuarioNode, "nome").getTextContent().trim());
		usr.setIp(XPathAPI.selectSingleNode(usuarioNode, "ip").getTextContent().trim());
		usr.setPorta(Integer.parseInt(XPathAPI.selectSingleNode(usuarioNode, "porta").getTextContent().trim()));
		usr.setOnline(Boolean.parseBoolean((XPathAPI.selectSingleNode(usuarioNode, "online").getTextContent().trim())));
		usr.setEmail(XPathAPI.selectSingleNode(usuarioNode, "email").getTextContent().trim());
		return usr;
	}
	/**
	 * Comunica-se com o servidor e traz a resposta
	 * @param com
	 * @param tipoRetorno
	 * @return respota em String se tipo == 1
	 *  @return respota em Objeto se tipo == 2
	 */
	public Object comunicarComServidor(String acao, Map<String, String> atributos) {
		StringBuffer respString = new StringBuffer("");
		Object resposta = null;
		try{
			
			URLConnection conn = abrirConexaoComServidor(acao);
			
			//O primeiro valor da lista vai com sujeira, por isso o artifício
			String data = "xx=xx";
				
			if(atributos != null){
				for (Map.Entry<String, String> item : atributos.entrySet()) {
					data += "&"+URLEncoder.encode(item.getKey(), "UTF-8")+"="+URLEncoder.encode(item.getValue(), "UTF-8"); 
				}
			}
			
			//Escreve no output para a pagina, POST
			ObjectOutputStream os = new ObjectOutputStream(conn.getOutputStream());
			os.writeBytes(data);
			os.flush();
			os.close();
			
			if ( true){
				//Busca a resposta da pagina e retorna a string
				BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));  
				String line;
				while ((line = rd.readLine()) != null) {  
					respString.append(line + "\n");
				} 
				
				resposta = URLDecoder.decode(respString.toString(),"UTF-8");
				rd.close();  
			}else if(false){ //Busca a resposta em objeto
				ObjectInputStream in = new ObjectInputStream(conn.getInputStream());
				List<Usuario> listaContatos = new ArrayList<Usuario>();
				Usuario usr = null;
				while(true){
					usr = (Usuario)in.readObject();
					if(usr == null) break;
					listaContatos.add(usr);
				}
				
				in.close();
			}
		}catch (Exception e) {
			e.printStackTrace();
		} 
		
		return resposta;
	}
	
	/**
	 * Cria uma conexão, testa primeiro o servidor local, depois testa o servidor na rede
	 * @param acao, comando a ser executado
	 * @return conexão ocm o servidor
	 */
	private URLConnection abrirConexaoComServidor(String acao){
		String endereco = DomineDuplex.SERVIDOR_LOCAL+acao;
		URL url = null;
		URLConnection con = null;
		try {
			url = new URL(endereco);
			con = url.openConnection();
			con.setDoOutput(true);
			con.setDoInput(true);
			con.connect();
		}catch (Exception e) {
			e.printStackTrace();
			try{
				endereco = DomineDuplex.SERVIDOR+acao;
				System.out.println("Endereco = "+endereco);
				url = new URL(endereco);
				con = url.openConnection();
				con.setDoOutput(true);
				con.setDoInput(true);
				con.connect();
			}catch (MalformedURLException e2) {
				e2.printStackTrace();
			} catch (IOException e3) {
				e3.printStackTrace();
			}
		}
		
		return con;
	}
	public List<Usuario> getListaContatos() {
		return listaContatos;
	}	
	
	private String usuarioToXML(Usuario usuario){
		
		StringBuffer xml = new StringBuffer();
		xml.append("\t<usuario>\n");
		xml.append("\t\t<nome>\n");
		xml.append("\t\t\t"+usuario.getNome()+"\n");
		xml.append("\t\t</nome>\n");
		xml.append("\t\t<ip>\n");
		xml.append("\t\t\t"+usuario.getIp()+"\n");
		xml.append("\t\t</ip>\n");
		xml.append("\t\t<online>\n");
		xml.append("\t\t\t"+usuario.isOnline()+"\n");
		xml.append("\t\t</online>\n");
		xml.append("\t\t<porta>\n");
		xml.append("\t\t\t"+usuario.getPorta()+"\n");
		xml.append("\t\t</porta>\n");
		xml.append("\t\t<email>\n");
		xml.append("\t\t\t"+usuario.getEmail()+"\n");
		xml.append("\t\t</email>\n");
		xml.append("\t</usuario>\n");
		return xml.toString();
		
	}
}
